﻿using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using EIP.Common.Core.Log;
using EIP.Common.Core.Utils;
using EIP.Common.Dapper;
using EIP.Common.Dapper.AdoNet;
using EIP.Common.Dapper.Extensions.Common;
using EIP.Common.Dapper.Extensions.DBUtility;
using EIP.Common.Entities;
using EIP.Common.Entities.CustomAttributes;
using EIP.Common.Entities.Paging;
using Newtonsoft.Json;

namespace EIP.Common.DataAccess
{
    /// <summary>
    ///     DapperRepository仓储,T代表实体信息,规范约束为T必须继承IEntityBase接口
    /// </summary>
    /// <typeparam name="T">实体</typeparam>
    public class DapperAsyncRepository<T> : BaseRepository, IAsyncRepository<T> where T : class, new()
    {
        private DataLogHandler _dataLoginHandler;

        #region 修改

        /// <summary>
        ///     更新
        /// </summary>
        /// <param name="current">实体信息</param>
        /// <returns>影响条数</returns>
        public virtual Task<int> UpdateAsync(T current)
        {
            //查询缓存中的Sql语句
            var tableInfo = DbEntityUtils.GetTableInfo(current, DbOperateType.Update);
            WriteDataLog(OperateType.编辑, tableInfo, current);
            var strSql = DbEntityUtils.GetUpdateSql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql, current);
        }

        #endregion

        #region 增加

        /// <summary>
        ///     插入
        /// </summary>
        /// <param name="entity">实体信息</param>
        /// <returns></returns>
        public virtual Task<int> InsertAsync(T entity)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.Insert);
            WriteDataLog(OperateType.新增, tableInfo, entity);
            var strSql = DbEntityUtils.GetInsertSql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql, entity);
        }

        /// <summary>
        ///     插入
        /// </summary>
        /// <param name="entity">实体信息</param>
        /// <returns></returns>
        public virtual Task<int> InsertScalarAsync(T entity)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.Insert);
            WriteDataLog(OperateType.新增, tableInfo, entity);
            var strSql = DbEntityUtils.GetInsertScalarSql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteExecuteScalarSql<T>(strSql, entity);
        }

        /// <summary>
        ///     批量插入
        /// </summary>
        /// <typeparam name="T">实体信息</typeparam>
        /// <param name="list">实体集合</param>
        /// <returns>影响条数</returns>
        public virtual Task<int> InsertMultipleDapperAsync(IEnumerable<T> list)
        {
            //查询缓存中的Sql语句
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Insert);
            WriteInsertMultipleDapperLog(tableInfo, list);
            var strSql = DbEntityUtils.GetInsertSql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql, list);
        }

        /// <summary>
        /// 写入新增日志
        /// </summary>
        /// <param name="tableInfo"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        private async Task WriteInsertMultipleDapperLog(TableInfo tableInfo, IEnumerable<T> list)
        {
            _dataLoginHandler = new DataLogHandler((byte)OperateType.新增, tableInfo.TableName, JsonConvert.SerializeObject(list));
            _dataLoginHandler.WriteLog();
        }

        /// <summary>
        ///     批量插入
        /// </summary>
        /// <typeparam name="T">实体信息</typeparam>
        /// <param name="list">实体集合</param>
        /// <returns>影响条数</returns>
        public virtual Task<int> InsertMultipleAsync(IEnumerable<T> list)
        {
            WriteInsertMultipleLog(list);
            return Task.Run(() => AdoUtil.InsertList(list.ToList()));
        }

        /// <summary>
        /// 写入新增日志
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private async Task WriteInsertMultipleLog(IEnumerable<T> list)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Insert);
            _dataLoginHandler = new DataLogHandler((byte)OperateType.新增, tableInfo.TableName, JsonConvert.SerializeObject(list));
            _dataLoginHandler.WriteLog();
        }
        #endregion

        #region 删除

        /// <summary>
        ///     根据实体信息删除
        /// </summary>
        /// <param name="entity">实体信息</param>
        /// <returns>影响条数</returns>
        public virtual Task<int> DeleteAsync(T entity)
        {
            //查询缓存中的Sql语句
            var tableInfo = DbEntityUtils.GetTableInfo(entity, DbOperateType.Delete);
            WriteDataLog(OperateType.删除, tableInfo, entity);
            var strSql = DbEntityUtils.GetDeleteByPropertySql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql, entity);
        }

        /// <summary>
        ///     根据Id删除
        /// </summary>
        /// <param name="id">主键Id</param>
        /// <returns>影响条数</returns>
        public virtual Task<int> DeleteAsync(object id)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Delete);
            WriteDeleteLog(tableInfo, id);
            var strSql = DbEntityUtils.GetDeleteByIdSql(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql, new { Id = id });
        }

        /// <summary>
        /// 写入删除
        /// </summary>
        /// <param name="tableInfo"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        private async Task WriteDeleteLog(TableInfo tableInfo, object id)
        {
            _dataLoginHandler = new DataLogHandler((byte)OperateType.删除, tableInfo.TableName, JsonConvert.SerializeObject(Task.Run(async () => await GetByIdAsync(id)).Result));
            _dataLoginHandler.WriteLog();
        }

        /// <summary>
        ///     批量删除
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public virtual Task<int> DeleteBatchAsync(string ids)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Delete);
            DeleteBatchLog(tableInfo, ids);
            var strSql = DbEntityUtils.GetDeleteBatchSql(tableInfo, ids);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql);
        }

        /// <summary>
        /// 写入删除
        /// </summary>
        /// <param name="tableInfo"></param>
        /// <param name="ids"></param>
        /// <returns></returns>
        private async Task DeleteBatchLog(TableInfo tableInfo, string ids)
        {
            _dataLoginHandler = new DataLogHandler((byte)OperateType.删除, tableInfo.TableName, JsonConvert.SerializeObject(ids));
            _dataLoginHandler.WriteLog();
        }

        /// <summary>
        /// 删除所有
        /// </summary>
        /// <returns></returns>
        public virtual Task<int> DeleteAllAsync()
        {
            //查询缓存中的Sql语句
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Delete);
            WriteDataLog(OperateType.删除, tableInfo);
            var strSql = DbEntityUtils.DeleteAll(tableInfo);
            return SqlMapperUtil.InsertUpdateOrDeleteSql<T>(strSql);
        }

        #endregion

        #region 查询

        /// <summary>
        ///     获取所有信息
        /// </summary>
        /// <returns></returns>
        public virtual Task<IEnumerable<T>> GetAllEnumerableAsync()
        {
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Select);
            var strSql = DbEntityUtils.GetFindAllSql(tableInfo);
            return SqlMapperUtil.SqlWithParamsDapper<T>(strSql);
        }

        /// <summary>
        ///     根据主句Id获取实体信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual Task<T> GetByIdAsync(object id)
        {
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Select);
            var strSql = DbEntityUtils.GetFindByIdSql(tableInfo);
            return SqlMapperUtil.SqlWithParamsSingle<T>(strSql, new { Id = id });
        }

        /// <summary>
        ///     从数据库获取数据
        /// </summary>
        /// <param name="current"></param>
        /// <returns></returns>
        public virtual Task<T> GetByIdFromDataBase(T current)
        {
            //查询缓存中的Sql语句
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Select);
            var strSql = DbEntityUtils.GetFindByIdSql(tableInfo);
            return SqlMapperUtil.SqlWithParamsSingle<T>(strSql, new
            {
                Id = AssemblyUtil.GetObjectPropertyValue(current, tableInfo.Id.Key)
            });
        }

        /// <summary>
        ///     复杂查询分页
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="querySql">查询语句</param>
        /// <param name="queryParam">查询参数</param>
        /// <returns>分页结果</returns>
        /// <remarks>
        ///     注意事项：
        ///     1.sql语句中需要加上@where、@orderBy、@rowNumber、@recordCount标记
        ///     如: "select *, @rowNumber, @recordCount from ADM_Rule @where"
        ///     2.实体中需增加扩展属性，作记录总数输出：RecordCount
        ///     3.标记解释:
        ///     @where：      查询条件
        ///     @orderBy：    排序
        ///     @x：          分页记录起点
        ///     @y：          分页记录终点
        ///     @recordCount：记录总数
        ///     @rowNumber：  行号
        ///     4.示例参考:
        /// </remarks>
        public virtual async Task<PagedResults<T>> PagingQueryAsync<T>(string querySql, QueryParam queryParam)
        {
            var sql = queryParam.IsReport ?
                string.Format(@"select * from ({0}) seq ", querySql) :
                string.Format(@"select * from ({0}) seq where seq.rownum between @x and @y", querySql);
            var currentPage = queryParam.Page; //当前页号
            var pageSize = queryParam.Rows; //每页记录数
            var lower = ((currentPage - 1) * pageSize) + 1; //记录起点
            var upper = currentPage * pageSize; //记录终点
            var where = @" where 1=1 ";
            if (!string.IsNullOrEmpty(queryParam._filters))
            {
                where += queryParam.Filters;
            }
            var parms = new DynamicParameters();
            parms.Add("x", lower);
            parms.Add("y", upper);
            //排序字段
            var orderString = string.Format("{0} {1}", queryParam.Sidx, queryParam.Sord);
            sql = sql.Replace("@recordCount", " count(*) over() as RecordCount ")
                .Replace("@rowNumber", " row_number() over (order by @orderBy) as rownum ")
                .Replace("@orderBy", orderString)
                .Replace("@where", where);
            var data = (await SqlMapperUtil.SqlWithParamsDapper<T>(sql, parms)).ToList();
            var pagerInfo = new PagerInfo();
            var first = data.FirstOrDefault();
            //记录总数
            if (first != null)
                pagerInfo.RecordCount = (int)first.GetType().GetProperty("RecordCount").GetValue(first, null);
            pagerInfo.Page = queryParam.Page;
            pagerInfo.PageCount = (pagerInfo.RecordCount + queryParam.Rows - 1) / queryParam.Rows; //页总数 
            return new PagedResults<T> { Data = data, PagerInfo = pagerInfo };
        }

        /// <summary>
        /// 单表存储过程分页
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="queryParam">分页参数</param>
        /// <returns>返回值</returns>
        public async Task<PagedResults<T>> PagingQueryProcAsync(QueryParam queryParam)
        {
            //表信息
            var tableInfo = DbEntityUtils.GetTableInfo(new T(), DbOperateType.Select);
            //类型
            var parms = new DynamicParameters();
            parms.Add("TableName", tableInfo.TableName);
            parms.Add("PrimaryKey", tableInfo.Id.Key);
            parms.Add("Fields", "*");
            parms.Add("Filters", queryParam.Filters);
            parms.Add("PageIndex", queryParam.Page);
            parms.Add("PageSize", queryParam.Rows);
            parms.Add("Sort", queryParam.Sidx + " " + queryParam.Sord);
            parms.Add("RecordCount", dbType: DbType.Int32, direction: ParameterDirection.Output);
            var pagerInfo = new PagerInfo();
            var data = (await SqlMapperUtil.StoredProcWithParams<T>("System_Proc_Paging", parms)).ToList();
            pagerInfo.RecordCount = parms.Get<int>("RecordCount");
            pagerInfo.Page = queryParam.Page;
            pagerInfo.PageCount = (pagerInfo.RecordCount + queryParam.Rows - 1) / queryParam.Rows; //页总数 
            return new PagedResults<T> { Data = data, PagerInfo = pagerInfo };
        }

        /// <summary>
        /// 自定义存储过程分页
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        /// <param name="queryParam">分页参数</param>
        /// <returns>返回值</returns>
        public async Task<PagedResults<T>> PagingQueryProcCustomAsync(QueryParam queryParam)
        {
            var parms = new DynamicParameters();
            parms.Add("TableName", queryParam.TableName);
            parms.Add("PrimaryKey", queryParam.PrimaryKey);
            parms.Add("Fields", queryParam.Fields);
            parms.Add("Filters", queryParam.Filters);
            parms.Add("PageIndex", queryParam.Page);
            parms.Add("PageSize", queryParam.Rows);
            parms.Add("Sort", queryParam.Sidx + " " + queryParam.Sord);
            parms.Add("RecordCount", dbType: DbType.Int32, direction: ParameterDirection.Output);
            var pagerInfo = new PagerInfo();
            var data = (await SqlMapperUtil.StoredProcWithParams<T>("System_Proc_Paging", parms)).ToList();
            pagerInfo.RecordCount = parms.Get<int>("RecordCount");
            pagerInfo.Page = queryParam.Page;
            pagerInfo.PageCount = (pagerInfo.RecordCount + queryParam.Rows - 1) / queryParam.Rows; //页总数 
            return new PagedResults<T> { Data = data, PagerInfo = pagerInfo };
        }
        #endregion

        #region 写入日志
        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="operateType"></param>
        /// <param name="tableInfo"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        private async Task WriteDataLog(OperateType operateType, TableInfo tableInfo, T current = null)
        {
            string operateAfterData = string.Empty;
            if (operateType == OperateType.编辑)
            {
                operateAfterData = JsonConvert.SerializeObject(Task.Run(async () => await GetByIdFromDataBase(current)).Result);
            }
            _dataLoginHandler = new DataLogHandler((byte)operateType, tableInfo.TableName, JsonConvert.SerializeObject(current), operateAfterData);
            _dataLoginHandler.WriteLog();
        }
        #endregion

    }
}